package alcatraz.common;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.logging.ConsoleHandler;
import java.util.logging.StreamHandler;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.logging.MemoryHandler;
import java.util.logging.SimpleFormatter;

/**
 * logger class of game
 *
 * @author andreas
 */
public class AlcatrazLogger implements IAlcatraz {

    /** reference to own (singelton) object */
    private static AlcatrazLogger logger = null;
    @SuppressWarnings("NonConstantLogger")
    /** reference to (common) logger */
    private Logger log = null;
    /** reference to consolen handler (if log to console) */
    private StreamHandler conHandler = null;
    /** reference to file handler (if log to log file) */
    private FileHandler file = null;
    /** reference to memory handler (if log to log file) */
    private MemoryHandler memHandler = null;

    /**
     * instance an object of class GameLogger if not already exist
     * otherwise delivers an object reference (singelton solution)
     *
     * @return reference to own (singelton) object
     */
    public static AlcatrazLogger getLogger() {
        if (logger == null) {
            logger = new AlcatrazLogger();
        }
        return logger;
    }

    /**
     * constructor (private because singelton class)
     */
    private AlcatrazLogger() { // singelton class
        initLogger();
    }

    /**
     * initialize game logger depended on flag 'LOG2CONSOLE' (look interface 
     * GameConstants): true - divert log to console output / 
     * false - divert log to log file
     */
    private void initLogger() {

        try {
            // inizialize log manager
            LogManager logManager = LogManager.getLogManager();
            // get log configuration data from assignment property file
            logManager.readConfiguration(new FileInputStream(LOG_CONFIG));

            // get reference to (common) logger
            log = Logger.getLogger("global");

            if(LOG2CONSOLE) {
                //LOG2CONSOLE=TRUE -> divert log to console
                //conHandler = new ConsoleHandler();
                conHandler = new StreamHandler(System.out, new AlcatrazFormater());
                conHandler.setLevel(LOG_LEVEL);
                log.addHandler(conHandler);
            } else {
                //LOG2CONSOLE=FALSE -> divert log to log file
                file = new FileHandler(LOG_FILE, true);
                memHandler = new MemoryHandler(file, 100, LOG_LEVEL);
                log.addHandler(memHandler);
            }
        } catch (IOException e) {
            // IO exception -> close open stream; print this severe execption
            // to consolen output and exit applicaton
            close();
            Util.printSevereException(e, true);
            System.exit(APPL_FAIL);
        } catch (SecurityException e) {
            // IO exception -> close open stream; print this severe execption
            // to consolen output and exit applicaton
            close();
            Util.printSevereException(e, true);
            System.exit(APPL_FAIL);
        }
        // set assignment log level (look interface GameConstants)
        log.setLevel(LOG_LEVEL);
    }

    /**
     * close game logger depended on flag 'LOG2CONSOLE' (look interface
     * GameConstants): true - close log to console output /
     * false - close log to log file
     */
    public void close() {
        if(LOG2CONSOLE) {
            //LOG2CONSOLE=TRUE -> close log to console
            if(conHandler!=null) conHandler.close();
        } else {
            //LOG2CONSOLE=FALSE -> close log to file
            if(memHandler!=null) memHandler.close();
            if(file!=null) file.close();
        }
    }

    /**
     * perform log statement
     *
     * @param level level of current log statement
     * @param msg message of current log statement
     */
    public void log(Level level, String msg) {
        log.log(level, msg);
        if(LOG2CONSOLE && conHandler!=null) {
            conHandler.flush();
        }
    }

    /**
     * perform log statement with additional info of class- and methode name
     *
     * @param classname name of class where call this methode
     * @param methode name of methode where call this methode
     * @param level level of current log statement
     * @param msg message of current log statement
     */
    public void log(String classname, String methode, Level level, String msg) {
        if(Util.isEmpty(classname)) {
            log.log(level, msg);
        } else {
            String message = classname.concat(".").concat(methode).
                    concat(": ").concat(msg);
            log.log(level, message);
        }
    }

    /**
     * perform log statement as severe level
     *
     * @param msg message of severe log statement
     * @param thrown exception which cause this severe log statement
     */
    public void severe(String msg, Throwable thrown) {
        log.log(Level.SEVERE, msg, thrown);
    }

    /**
     * perform log statement as severe level with additional info of
     * class- and methode name
     *
     * @param classname name of class where call this methode
     * @param methode name of methode where call this methode
     * @param msg message of severe log statement
     * @param thrown exception which cause this severe log statement
     */
    public void severe(String classname, String methode, String msg, Throwable thrown) {
        if(Util.isEmpty(classname)) {
            log.log(Level.SEVERE, msg);
        } else {
            String message = classname.concat(".").concat(methode).
                    concat(": ").concat(msg);
            log.log(Level.SEVERE, message);
        }
        log.log(Level.SEVERE, msg, thrown);
    }
}
